3. Plotting¶
Matplotlib is a standard plotting library of python. We begin by importing it first along with numpy.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
The most widely used function in matplotlib is plot, which allows you to plot 1D and 2D data. Here is a simple example:
# Compute the x and y coordinates for points on a sine curve
x = np.arange(0, 3 * np.pi, 0.1)
y = np.sin(x)
# Plot the points using matplotlib
plt.plot(x, y)
[<matplotlib.lines.Line2D at 0x7f55061a4210>]
We can easily customize the style of plots for poster/talk/paper
print(plt.style.available)
['Solarize_Light2', '_classic_test_patch', 'bmh', 'classic', 'dark_background', 'fast', 'fivethirtyeight', 'ggplot', 'grayscale', 'seaborn', 'seaborn-bright', 'seaborn-colorblind', 'seaborn-dark', 'seaborn-dark-palette', 'seaborn-darkgrid', 'seaborn-deep', 'seaborn-muted', 'seaborn-notebook', 'seaborn-paper', 'seaborn-pastel', 'seaborn-poster', 'seaborn-talk', 'seaborn-ticks', 'seaborn-white', 'seaborn-whitegrid', 'tableau-colorblind10']
plt.style.use(['seaborn-white'])
plt.plot(x, y)
[<matplotlib.lines.Line2D at 0x7f55060494d0>]
if we want to customize plots it is better to plot by first defining fig and ax objecs which have manuy methods for customizing figure resolution and plot related aspects respecticely.
fig, ax = plt.subplots()
y_sin = np.sin(x)
y_cos = np.cos(x)
# Plot the points using matplotlib
ax.plot(x, y_sin)
ax.plot(x, y_cos)
# Specify labels
ax.set_xlabel('x axis label')
ax.set_ylabel('y axis label')
ax.set_title('Sine and Cosine')
ax.legend(['Sine', 'Cosine'])
#fig.savefig("myfig.pdf")
<matplotlib.legend.Legend at 0x7f5506005e10>
3.1. A gallery of useful examples¶
For a greater variety of plotting examples check out Matploltib Gallery!
1D plotting is conveniently done by creating fig and ax objects which allow coutom styling plots and figure properties separately.
fig, ax = plt.subplots() # Create fig and ax objects
t = np.arange(0.0, 2*np.pi, 0.1) # create x values via np.arange or np.linspace
s = np.sin(t) # create y values
ax.plot(t, s, '-o') # make the plot
#fig.savefig('myFIG.png') # save figure
[<matplotlib.lines.Line2D at 0x7f5505efd110>]
3.1.1. fig and ax objects¶
For customizing plots it is more convenient to define fig and ax objects. One can then use ax object to make veriety of subplots then use fig to save the entire figure as one pdf. Try changing fig size, number of columns and rows.
t = np.arange(0.0, 2*np.pi, 0.1) # create x values
s = np.sin(t) # create y values
fig, ax = plt.subplots(nrows=1,ncols=2,figsize=(6,3))
ax[0].plot(t, s,'-o', color='purple', lw=1.0) # plot on subplot-1
ax[1].plot(t, s**2,'-o', color='green', lw=1.0) # plot on subplot-2
#fig.savefig('sd.png') # save the figure
[<matplotlib.lines.Line2D at 0x7f5505e3df90>]
3.1.2. Plotting in 2D¶
To make 2D plots we need to generate 2D grid \((x,y)\) of points and pass it to our function \(f(x,y)\)
x = np.arange(0.0, 2*np.pi, 0.1) # create x values
y = np.arange(0.0, 2*np.pi, 0.1) # create y values
X, Y = np.meshgrid(x,y) # tunring 1D array into 2D grids of x and y values
Z = np.sin(X) * np.cos(Y) # feed 2D grids to our 2D function f(x,y)
fig, ax = plt.subplots() # Create fig and ax objects
ax.pcolor(X, Y, Z,cmap='RdBu') # plot
# try also ax.contour, ax.contourf
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/ipykernel_launcher.py:9: MatplotlibDeprecationWarning: shading='flat' when X and Y have the same dimensions as C is deprecated since 3.3. Either specify the corners of the quadrilaterals with X and Y, or pass shading='auto', 'nearest' or 'gouraud', or set rcParams['pcolor.shading']. This will become an error two minor releases later.
if __name__ == '__main__':
<matplotlib.collections.PolyCollection at 0x7f5505e1af50>
3.1.3. 3D plots with matplotlib¶
from matplotlib import cm, colors
from mpl_toolkits.mplot3d import Axes3D
# Create fig and ax objects for 3d plotting
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
# Using X,Y,Z grid of points in previous step
ax.plot_surface(X, Y, Z, cmap='RdYlBu')
<mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x7f5505df82d0>
3.1.4. Plotting histograms with matplotlib¶
fig, ax = plt.subplots()
# Make up some random data
mu, sigma = 100, 15
x = mu + sigma * np.random.randn(10000)
# Plot 1D histogram of the data
hist = ax.hist(x, bins=40, density=True)
fig, ax = plt.subplots()
# Make up some random data
mu, sigma = 100, 15
x = mu + sigma * np.random.randn(10000)
y = mu + 2*sigma * np.random.randn(10000)
# Plot 2D histogram of the data
hist = ax.hist2d(x, y, bins=40, density=True, cmap='RdBu_r')
3.1.5. Statistical visualizations with seaborn¶
Seaborn For visualizing statistical plots there is a specialized library build on top of matplotlib that simplifies many intermediate steps that are needed to go from data to beautiful and polished visualization.
import seaborn as sns
from scipy import stats
# Make up some random data
mu, sigma = 100, 15
x = mu + sigma * np.random.randn(10000)
sns.displot(x, kind="kde")
<seaborn.axisgrid.FacetGrid at 0x7f54f51d57d0>
3.2. Pandas and seaborn! a power couple for multivariate statistics visualization¶
# Normal distribution of points
n_points=200
df = pd.DataFrame({ 'X': 1*np.random.randn(n_points),
'Y': 5*np.random.randn(n_points),
'Z': 1+5*np.random.randn(n_points),
'time': np.linspace(0,n_points,n_points)
})
sns.jointplot(data=df, x='X', y='Z',
kind="kde", cmap='RdBu_r')
<seaborn.axisgrid.JointGrid at 0x7f54f4930850>
3.3. Interactive plots¶
import plotly.express as px
3.3.1. Plotly¶
Plotly is large multi-language interactive graphing library that covers Python/Julia/R.
Plotly-dash is a framework for building web dashborads with itneractive plotly graphs.
Plotly-express is a high level library for quick visualizations whihc is similiar to seaborn vs matploltib in its philosophy
Check out this cool website built using Dash-Plotly
df = pd.DataFrame({ 'X': 1*np.random.randn(500),
'Y': 5*np.random.randn(500),
'Z': 1+5*np.random.randn(500),
'time': np.arange(500)
})
px.density_heatmap(df, x='X', y='Y')
#px.line(df, x='X', y='Y')
#px.scatter(df, x='X', y='Y')
#px.area(df, x='X', y='Y')
#px.histogram(df, x="X")
fig = px.scatter(df, x="X", y="Y", size=20*np.ones(len(df)),
animation_frame="time", animation_group='Y', color='Y',
range_x=[-20,20], range_y=[-20,20]
)
fig.show()
3.3.2. Holoviews¶
Stop plotting your data - annotate your data and let it visualize itself
Too many libraries for visualization in pyviz universe? Enter Holoviews! We all can agree there are just too many options for visualizing data and sometimes it is impossible to pick one and stick with becaue different libaries have different strengths when it comes visualizing different kinds of data. Plotly does a gread job with 3D visualization while matploltib is mostly desinged for 2D plots. Seaborn has everying one needs to genreate statistical plots while plotly may cover ony a subgroup of seaborn, etc. One emerging idea in scientific software desing is to create library agnostic tools. E.g if you want to plot histogram you can do it either using several different libraries or using a library agnsotic tool by specifiying the particular library interface for the visualization.
import holoviews as hv
hv.extension('matplotlib') # try 'bokeh' or 'matplotlib'
data = [1,2,4,8,16,25]
#hv.Curve(data) + hv.Scatter(data) # adding crates subplots
hv.Curve(data) * hv.Scatter(data) # multiplying creates overlay
import numpy as np
import pandas as pd
df = pd.DataFrame({'X':np.random.randn(1000),'Y':np.random.randn(1000), 'Z':np.random.randn(1000)})
data = df[['X', 'Y']].values
#hv.Curve(data)
hv.Scatter(data) + hv.Curve(data) + hv.Points(data) + hv.Bivariate(data)
#hv.Histogram(df['X'], density=True, bins=50)
#Example of 3D plot
Z = np.sin( np.random.randn(40,40) )
hv.Surface(Z, bounds=(-5, -5, 5, 5))
3.3.3. Using interactive widgets¶
Suppose we would like to explore how the variation of parameter \(\lambda\) affects the following function of a standing wave:
Make a python-function which creates a plot as a function of a parameter(s) of interest.
Add an interactive widget on top to vary the parameter.
from ipywidgets import widgets
@widgets.interact(phase=(0,2*np.pi), freq = (0.1,5))
def wave(phase=0, freq=0.5):
x = np.linspace(0,10,1000)
y = np.sin(freq*x+phase)
plt.plot(x, y)
hv.extension('matplotlib')
def wave(phase, freq):
x = np.linspace(0,10,1000)
y = np.sin(freq*x+phase)
return hv.Curve((x, y))
## Option-1: Define ranges of variables and pass to DynamicMap
#hv.DynamicMap(wave, kdims=['phase', 'freq']).redim.range(phase=(0.1,2*np.pi), freq=(0.1,5))
## Option-2: make dictionary of data points and pass to HoloMap
hv.HoloMap({ (phase, freq): wave(phase, freq)
for phase in np.linspace(0.0,2*np.pi,20)
for freq in np.linspace(0.1, 2, 20) },
kdims=['phase', 'freq'])
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/PIL/ImageFile.py in _save(im, fp, tile, bufsize)
499 try:
--> 500 fh = fp.fileno()
501 fp.flush()
AttributeError: '_idat' object has no attribute 'fileno'
During handling of the above exception, another exception occurred:
KeyboardInterrupt Traceback (most recent call last)
<ipython-input-25-eaa909fdcca0> in <module>
16 for phase in np.linspace(0.0,2*np.pi,20)
17 for freq in np.linspace(0.1, 2, 20) },
---> 18 kdims=['phase', 'freq'])
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/IPython/core/displayhook.py in __call__(self, result)
260 self.start_displayhook()
261 self.write_output_prompt()
--> 262 format_dict, md_dict = self.compute_format_data(result)
263 self.update_user_ns(result)
264 self.fill_exec_result(result)
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/IPython/core/displayhook.py in compute_format_data(self, result)
149
150 """
--> 151 return self.shell.display_formatter.format(result)
152
153 # This can be set to True by the write_output_prompt method in a subclass
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/IPython/core/formatters.py in format(self, obj, include, exclude)
148 return {}, {}
149
--> 150 format_dict, md_dict = self.mimebundle_formatter(obj, include=include, exclude=exclude)
151
152 if format_dict or md_dict:
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/decorator.py in fun(*args, **kw)
230 if not kwsyntax:
231 args, kw = fix(args, kw, sig)
--> 232 return caller(func, *(extras + args), **kw)
233 fun.__name__ = func.__name__
234 fun.__doc__ = func.__doc__
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/IPython/core/formatters.py in catch_format_error(method, self, *args, **kwargs)
222 """show traceback on failed format call"""
223 try:
--> 224 r = method(self, *args, **kwargs)
225 except NotImplementedError:
226 # don't warn on NotImplementedErrors
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj, include, exclude)
968
969 if method is not None:
--> 970 return method(include=include, exclude=exclude)
971 return None
972 else:
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/holoviews/core/dimension.py in _repr_mimebundle_(self, include, exclude)
1315 combined and returned.
1316 """
-> 1317 return Store.render(self)
1318
1319
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/holoviews/core/options.py in render(cls, obj)
1403 data, metadata = {}, {}
1404 for hook in hooks:
-> 1405 ret = hook(obj)
1406 if ret is None:
1407 continue
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in pprint_display(obj)
280 if not ip.display_formatter.formatters['text/plain'].pprint:
281 return None
--> 282 return display(obj, raw_output=True)
283
284
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in display(obj, raw_output, **kwargs)
256 elif isinstance(obj, (HoloMap, DynamicMap)):
257 with option_state(obj):
--> 258 output = map_display(obj)
259 elif isinstance(obj, Plot):
260 output = render(obj)
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in wrapped(element)
144 try:
145 max_frames = OutputSettings.options['max_frames']
--> 146 mimebundle = fn(element, max_frames=max_frames)
147 if mimebundle is None:
148 return {}, {}
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in map_display(vmap, max_frames)
204 return None
205
--> 206 return render(vmap)
207
208
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in render(obj, **kwargs)
66 renderer = renderer.instance(fig='png')
67
---> 68 return renderer.components(obj, **kwargs)
69
70
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/holoviews/plotting/renderer.py in components(self, obj, fmt, comm, **kwargs)
408 doc = Document()
409 with config.set(embed=embed):
--> 410 model = plot.layout._render_model(doc, comm)
411 if embed:
412 return render_model(model, comm)
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/panel/viewable.py in _render_model(self, doc, comm)
433 save_path=config.embed_save_path,
434 load_path=config.embed_load_path,
--> 435 progress=False)
436 else:
437 add_to_doc(model, doc)
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/panel/io/embed.py in embed_state(panel, model, doc, max_states, max_opts, json, json_prefix, save_path, load_path, progress, states)
331 with always_changed(config.safe_embed):
332 for w in ws:
--> 333 w.value = k
334 except Exception:
335 skip = True
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/param/parameterized.py in _f(self, obj, val)
316 instance_param.__set__(obj, val)
317 return
--> 318 return f(self, obj, val)
319 return _f
320
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/param/parameterized.py in __set__(self, obj, val)
934 old=_old, new=val, type=None)
935 for watcher in watchers:
--> 936 obj.param._call_watcher(watcher, event)
937 if not obj.param._BATCH_WATCH:
938 obj.param._batch_call_watchers()
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/param/parameterized.py in _call_watcher(self_, watcher, event)
1589 event = self_._update_event_type(watcher, event, self_.self_or_cls.param._TRIGGER)
1590 with batch_watch(self_.self_or_cls, enable=watcher.queued, run=False):
-> 1591 self_._execute_watcher(watcher, (event,))
1592
1593 def _batch_call_watchers(self_):
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/param/parameterized.py in _execute_watcher(self, watcher, events)
1571 async_executor(partial(watcher.fn, *args, **kwargs))
1572 else:
-> 1573 watcher.fn(*args, **kwargs)
1574
1575 def _call_watcher(self_, watcher, event):
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/panel/pane/holoviews.py in _widget_callback(self, event)
215 def _widget_callback(self, event):
216 for _, (plot, pane) in self._plots.items():
--> 217 self._update_plot(plot, pane)
218
219 #----------------------------------------------------------------
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/panel/pane/holoviews.py in _update_plot(self, plot, pane)
208 plot.update(key)
209 if hasattr(plot.renderer, 'get_plot_state'):
--> 210 pane.object = plot.renderer.get_plot_state(plot)
211 else:
212 # Compatibility with holoviews<1.13.0
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/param/parameterized.py in _f(self, obj, val)
316 instance_param.__set__(obj, val)
317 return
--> 318 return f(self, obj, val)
319 return _f
320
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/param/parameterized.py in __set__(self, obj, val)
934 old=_old, new=val, type=None)
935 for watcher in watchers:
--> 936 obj.param._call_watcher(watcher, event)
937 if not obj.param._BATCH_WATCH:
938 obj.param._batch_call_watchers()
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/param/parameterized.py in _call_watcher(self_, watcher, event)
1589 event = self_._update_event_type(watcher, event, self_.self_or_cls.param._TRIGGER)
1590 with batch_watch(self_.self_or_cls, enable=watcher.queued, run=False):
-> 1591 self_._execute_watcher(watcher, (event,))
1592
1593 def _batch_call_watchers(self_):
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/param/parameterized.py in _execute_watcher(self, watcher, events)
1571 async_executor(partial(watcher.fn, *args, **kwargs))
1572 else:
-> 1573 watcher.fn(*args, **kwargs)
1574
1575 def _call_watcher(self_, watcher, event):
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/panel/pane/base.py in _update_pane(self, *events)
183 if comm or state._unblocked(doc):
184 with unlocked():
--> 185 self._update_object(ref, doc, root, parent, comm)
186 if comm and 'embedded' not in root.tags:
187 push(doc, comm)
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/panel/pane/base.py in _update_object(self, ref, doc, root, parent, comm)
148 old_model = self._models[ref][0]
149 if self._updates:
--> 150 self._update(ref, old_model)
151 else:
152 new_model = self._get_model(doc, root, parent, comm)
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/panel/pane/plot.py in _update(self, ref, model)
191 def _update(self, ref=None, model=None):
192 if not self.interactive:
--> 193 model.update(**self._get_properties())
194 return
195 manager = self._managers[ref]
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/panel/pane/image.py in _get_properties(self)
100 if self.object is None:
101 return dict(p, text='<img></img>')
--> 102 data = self._img()
103 if not isinstance(data, bytes):
104 data = base64.b64decode(data)
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/panel/pane/plot.py in _img(self)
218 bbox_inches = None
219
--> 220 self.object.canvas.print_figure(b, bbox_inches=bbox_inches)
221 return b.getvalue()
222
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/matplotlib/backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
2259 orientation=orientation,
2260 bbox_inches_restore=_bbox_inches_restore,
-> 2261 **kwargs)
2262 finally:
2263 if bbox_inches and restore_bbox:
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/matplotlib/backend_bases.py in wrapper(*args, **kwargs)
1667 kwargs.pop(arg)
1668
-> 1669 return func(*args, **kwargs)
1670
1671 return wrapper
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/matplotlib/backends/backend_agg.py in print_png(self, filename_or_obj, metadata, pil_kwargs, *args)
509 mpl.image.imsave(
510 filename_or_obj, self.buffer_rgba(), format="png", origin="upper",
--> 511 dpi=self.figure.dpi, metadata=metadata, pil_kwargs=pil_kwargs)
512
513 def print_to_buffer(self):
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/matplotlib/image.py in imsave(fname, arr, vmin, vmax, cmap, format, origin, dpi, metadata, pil_kwargs)
1614 pil_kwargs.setdefault("format", format)
1615 pil_kwargs.setdefault("dpi", (dpi, dpi))
-> 1616 image.save(fname, **pil_kwargs)
1617
1618
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/PIL/Image.py in save(self, fp, format, **params)
2170
2171 try:
-> 2172 save_handler(self, fp, filename)
2173 finally:
2174 # do what we can to clean up
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/PIL/PngImagePlugin.py in _save(im, fp, filename, chunk, save_all)
1333 _write_multiple_frames(im, fp, chunk, rawmode)
1334 else:
-> 1335 ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
1336
1337 if info:
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/PIL/ImageFile.py in _save(im, fp, tile, bufsize)
512 else:
513 while True:
--> 514 l, s, d = e.encode(bufsize)
515 fp.write(d)
516 if s:
KeyboardInterrupt:
3.3.4. Interactive visualization of a Reaction-Diffusion system¶
def laplacian(Z, dx):
"""
Function to computes the discrete Laplace operator of
a 2D variable on the grid (using a five-point stencil
finite difference method.)
"""
Ztop = Z[0:-2,1:-1]
Zleft = Z[1:-1,0:-2]
Zbottom = Z[2:,1:-1]
Zright = Z[1:-1,2:]
Zcenter = Z[1:-1,1:-1]
return (Ztop + Zleft + Zbottom + Zright - 4 * Zcenter) / dx**2
def reaction_diffusion(a=2.8e-4, b=5e-3, tau=0.1, k=-0.005, samples=10):
"""
We simulate the PDE with the finite difference method.
The samples value is the number of equally spaced samples
to collect over the total simulation time T.
"""
size = 100 # size of the 2D grid
dx = 2./size # space step
T = 10.0 # total time
dt = 4.5 * dx**2 # simulation time step
n = int(T/dt)
result = []
U = np.random.rand(size, size)
V = np.random.rand(size, size)
sample_times = [int(el) for el in np.linspace(0, n, samples)]
for i in range(n):
# We compute the Laplacian of u and v.
deltaU = laplacian(U, dx=dx)
deltaV = laplacian(V, dx=dx)
# We take the values of u and v inside the grid.
Uc = U[1:-1,1:-1]
Vc = V[1:-1,1:-1]
# We update the variables.
U[1:-1,1:-1], V[1:-1,1:-1] = \
Uc + dt * (a * deltaU + Uc - Uc**3 - Vc + k), \
Vc + dt * (b * deltaV + Uc - Vc) / tau
# Neumann conditions: derivatives at the edges
# are null.
for Z in (U, V):
Z[0,:] = Z[1,:]
Z[-1,:] = Z[-2,:]
Z[:,0] = Z[:,1]
Z[:,-1] = Z[:,-2]
if i in sample_times:
result.append((i * dt,U.copy()))
return result
sim1 = reaction_diffusion()
hv.HoloMap({time: hv.Image(array) for (time, array) in sim1}, kdims=['Time'])
3.4. Additional resoruces.¶
Matplotlib has a huge scientific user base. This means that you can always find a good working template of any kind of visualization which you want to make. With basic understanding of matplotlib and some solid googling skills you can go very far. Here are some additional resources that you may find helpful